# 导入系统模块
import os
import time
from datetime import datetime
from io import BytesIO
import logging
import re
# 导入第三方库
from bs4 import BeautifulSoup
import requests
# 导入Django模块
from django.http import HttpResponse, HttpResponseBadRequest, JsonResponse, FileResponse
from django.views.generic import View
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import render
from django.conf import settings
from django.db.models import F
from urllib.parse import quote
import base64
# 导入Aspose.Words模块（用于Word文档操作）
import aspose.words as aw
from aspose.words import Document, DocumentBuilder, ImportFormatMode, SaveFormat
from aspose.words.drawing import RelativeHorizontalPosition, RelativeVerticalPosition, WrapType
# 导入LaTeX转MathML模块
from latex2mathml.converter import convert as latex_to_mathml
# 导入模型
from core.models.mistake import Mistake

# 初始化日志记录器
logger = logging.getLogger(__name__)

def mistake_export_word(request):
    """
    导出错题内容为Word文档
    
    参数:
        request: HTTP请求对象，包含要导出的HTML内容
    
    返回:
        FileResponse: 包含Word文档的响应
        或 JsonResponse: 错误信息
    """
    print("导出开始")
    if request.method == 'POST':
        try:
            # 获取要导出的HTML内容
            export_content = request.POST.get('export_content', '')
            
            # 验证内容是否为空
            if not export_content:
                return JsonResponse({'status': 'error', 'message': '没有可导出的内容'}, status=400)

            # 创建Word文档
            doc = Document()
            builder = DocumentBuilder(doc)
            export_date=datetime.now()
            # 添加导出日期
            builder.writeln(f'导出日期: {export_date.strftime("%Y-%m-%d %H:%M:%S")}')
            # 插入分页符
            builder.insert_break(aw.BreakType.PAGE_BREAK)

            # 直接插入HTML内容到Word文档
            builder.insert_html(export_content)

            # 保存文档到内存缓冲区
            buffer = BytesIO()
            doc.save(buffer, aw.SaveFormat.DOCX)
            buffer.seek(0)

            # 准备响应
            if not request.user.is_authenticated:
                raise Exception("用户未登录")
            username = request.user.username
            if not username:
                username = '未知用户'
            # 生成文件名
            filename = f'{username}_{export_date.strftime("%Y%m%d")}_错题集.docx'
            
            # 创建文件响应
            response = FileResponse(
                buffer,
                as_attachment=True,
                filename=quote(filename.encode('utf-8')),
                content_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document"
            )
            return response
        except Exception as e:
            error_msg = f"导出Word文档失败: {str(e)}"
            print(error_msg)
            logger.error(error_msg, exc_info=True)
            return JsonResponse({'status': 'error', 'message': error_msg})
    # 非POST请求处理
    return HttpResponseNotAllowed(['POST'])

class MistakeExportPreviewView(LoginRequiredMixin, View):
    """
    错题导出预览视图类
    
    提供错题导出前的预览功能和生成Word文档的功能
    """
    http_method_names = ["get", "post"]  # 允许的HTTP方法
    template_name = "mistake/mistake_export_preview.html"  # 预览模板

    def get(self, request, *args, **kwargs):
        """
        处理GET请求，显示错题导出预览页面
        
        参数:
            request: HTTP请求对象，包含选中的错题ID
        
        返回:
            HttpResponse: 包含预览页面的响应
        """
        # 获取选中的错题ID列表
        mistake_ids = request.GET.get("mistake_ids", "").split(",")
        if not mistake_ids or mistake_ids == [""]:
            return HttpResponseBadRequest("未选择任何错题")

        # 查询选中的错题
        mistakes = Mistake.objects.filter(id__in=mistake_ids)
        return render(
            request,
            self.template_name,
            {"mistakes": mistakes, "mistake_ids": ",".join(mistake_ids)},
        )

    def post(self, request, *args, **kwargs):
        """
        处理POST请求，生成并下载Word文档
        
        参数:
            request: HTTP请求对象，包含选中的错题ID
        
        返回:
            HttpResponse: 包含Word文档的响应
        """
        # 确认导出并生成Word文档
        mistake_ids = request.POST.get("mistake_ids", "").split(",")
        if not mistake_ids or mistake_ids == [""]:
            return HttpResponseBadRequest("未选择任何错题")

        # 查询选中的错题
        mistakes = Mistake.objects.filter(id__in=mistake_ids)

        # 创建Word文档
        doc = Document()
        builder = DocumentBuilder(doc)
        builder.paragraph_format.style = doc.styles.get_by_name("Heading 1")
        builder.write("错题集")
        builder.writeln()
        builder.paragraph_format.style = doc.styles.get_by_name("Normal")  # 恢复正常样式
        builder.writeln(f'导出日期: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}')
        builder.writeln(f"共 {len(mistakes)} 道错题")
        builder.insert_break(aw.BreakType.PAGE_BREAK)  # 插入分页符

        # 添加错题内容并更新引用计数
        for idx, mistake in enumerate(mistakes, 1):
            self._add_mistake_to_doc(doc, mistake, idx)
            # 更新引用计数（使用F表达式避免竞态条件）
            mistake.citation_count = F("citation_count") + 1
            mistake.save()

        # 刷新数据以获取更新后的引用计数
        Mistake.objects.filter(id__in=mistake_ids).update()

        # 将文档保存到内存
        buffer = BytesIO()
        doc.save(buffer, aw.SaveFormat.DOCX)
        buffer.seek(0)

        # 准备响应
        response = HttpResponse(
            buffer.getvalue(),
            content_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
        )
        response["Content-Disposition"] = (
            f'attachment; filename="错题集_{datetime.now().strftime("%Y%m%d")}.docx"'
        )
        return response

    def _add_mistake_to_doc(self, doc, mistake, index):
        """
        将单个错题添加到Word文档中
        
        参数:
            doc: Word文档对象
            mistake: 错题对象
            index: 错题序号
        """
        builder = DocumentBuilder(doc)
        # 添加错题标题
        builder.paragraph_format.style = doc.styles.get_by_name("Heading 2")
        builder.write(f"错题 {index}: {mistake.title}")
        builder.writeln()
        builder.paragraph_format.style = doc.styles.get_by_name("Normal")

        # 添加基础信息
        # 移除start_paragraph()调用，因为aspose.words.DocumentBuilder没有此方法
        builder.bold = True
        builder.write("学科: ")
        builder.bold = False
        builder.write(mistake.subject.name if mistake.subject else "未分类")
        builder.bold = True
        builder.write(" | 知识点: ")
        builder.bold = False
        builder.write(
            mistake.knowledge_point.name if mistake.knowledge_point else "未分类"
        )
        builder.bold = True
        builder.write(" | 难度: ")
        builder.bold = False
        builder.write("★" * mistake.star_rating)
        # 移除end_paragraph()调用，因为aspose.words.DocumentBuilder没有此方法

        # 添加题目内容
        builder.paragraph_format.style = doc.styles.get_by_name("Heading 3")
        builder.write("题目内容:")
        builder.writeln()
        builder.paragraph_format.style = doc.styles.get_by_name("Normal")
        self._process_content_with_formulas(builder, mistake.question)

        # 添加正确答案
        builder.paragraph_format.style = doc.styles.get_by_name("Heading 3")
        builder.write("正确答案:")
        builder.writeln()
        builder.paragraph_format.style = doc.styles.get_by_name("Normal")
        self._process_content_with_formulas(builder, mistake.answer)

        # 添加解析
        builder.paragraph_format.style = doc.styles.get_by_name("Heading 3")
        builder.write("解析:")
        builder.writeln()
        builder.paragraph_format.style = doc.styles.get_by_name("Normal")
        # self._process_content_with_formulas(builder, mistake.analysis)

        # 添加分隔线和分页符
        builder.insert_paragraph()
        builder.write("---")
        builder.insert_break(aw.BreakType.PAGE_BREAK)

    def _process_content_with_formulas(self, builder, html_content):
        """
        处理包含公式的HTML内容
        
        参数:
            builder: DocumentBuilder对象
            html_content: 包含LaTeX公式的HTML内容
        """
        # 处理LaTeX公式转换为MathML
        html_content = self._convert_latex_to_mathml(html_content)

        # 解析HTML内容以处理图片
        soup = BeautifulSoup(html_content, "html.parser")
        elements = soup.contents

        for element in elements:
            if element.name == "img":
                # 处理图片
                self._add_image_to_doc(builder, element)
            else:
                # 插入其他HTML内容
                if str(element).strip():
                    html = str(element).strip()
                    if html:
                        builder.insert_html(html)
                        # 仅在块级元素后添加段落
                        if element.name in ["p", "div", "h1", "h2", "h3", "ul", "ol"]:
                            builder.insert_paragraph()

    def _convert_latex_to_mathml(self, html_content):
        """
        将HTML内容中的LaTeX公式转换为MathML
        
        参数:
            html_content: 包含LaTeX公式的HTML内容
        
        返回:
            str: 转换后的HTML内容
        """
        # 查找LaTeX公式（支持$$...$$和$...$格式）
        latex_patterns = [
            (re.compile(r"\$\$(.*?)\$\$", re.DOTALL), False),  # 多行公式
            (re.compile(r"\$(.*?)\$", re.DOTALL), True),  # 行内公式
        ]

        for pattern, inline in latex_patterns:
            for latex in pattern.findall(html_content):
                # 将LaTeX转换为MathML
                mathml = latex_to_mathml(latex.strip())
                # 包装MathML为适当的HTML标签
                if inline:
                    html_math = f"<span>{mathml}</span>"
                else:
                    html_math = f'<p style="text-align:center">{mathml}</p>'
                # 替换LaTeX为MathML
                html_content = html_content.replace(
                    f"${latex}$" if inline else f"$$${latex}$$$", html_math
                )

        return html_content

    def _add_image_to_doc(self, builder, img_tag):
        """
        将图片添加到Word文档中
        
        参数:
            builder: DocumentBuilder对象
            img_tag: 图片HTML标签对象
        """
        src = img_tag.get("src", "")
        try:
            if src.startswith("data:image"):
                # 处理base64图片
                header, base64_data = src.split(",", 1)
                # 修复URL安全的Base64编码
                base64_data = base64_data.replace("-", "+").replace("_", "/")
                # 处理填充问题
                padding = len(base64_data) % 4
                if padding:
                    base64_data += "=" * (4 - padding)
                try:
                    # 解码Base64数据
                    img_data = base64.b64decode(base64_data, validate=True)
                except Exception as e:
                    logger.error(f"Base64解码失败: {str(e)}")
                    builder.writeln("[图片解码失败]")
                    return
                stream = BytesIO(img_data)

                # 插入图片并设置格式
                shape = builder.insert_image(stream)
                # 设置图片大小（最大宽度5英寸）
                max_width = aw.ConvertUtil.inch_to_point(5)
                if shape.width > max_width:
                    ratio = max_width / shape.width
                    shape.width = max_width
                    shape.height = shape.height * ratio
                shape.wrap_type = WrapType.INLINE
                shape.relative_horizontal_position = RelativeHorizontalPosition.PAGE
                shape.horizontal_alignment = aw.drawing.HorizontalAlignment.CENTER

            elif src.startswith("http"):
                # 处理网络图片
                response = requests.get(src, timeout=10)
                response.raise_for_status()
                stream = BytesIO(response.content)

                shape = builder.insert_image(stream)
                # 设置图片大小（最大宽度5英寸）
                max_width = aw.ConvertUtil.inch_to_point(5)
                if shape.width > max_width:
                    ratio = max_width / shape.width
                    shape.width = max_width
                    shape.height = shape.height * ratio
                shape.wrap_type = WrapType.INLINE
                shape.relative_horizontal_position = RelativeHorizontalPosition.PAGE
                shape.horizontal_alignment = aw.drawing.HorizontalAlignment.CENTER

            else:
                # 处理本地图片
                img_path = os.path.join(settings.MEDIA_ROOT, src)
                if os.path.exists(img_path):
                    shape = builder.insert_image(img_path)
                    # 设置图片大小（最大宽度5英寸）
                    max_width = aw.ConvertUtil.inch_to_point(5)
                    if shape.width > max_width:
                        ratio = max_width / shape.width
                        shape.width = max_width
                        shape.height = shape.height * ratio
                    shape.wrap_type = WrapType.INLINE
                    shape.relative_horizontal_position = RelativeHorizontalPosition.PAGE
                    shape.horizontal_alignment = aw.drawing.HorizontalAlignment.CENTER
                else:
                    builder.writeln(f"[图片缺失: {src}]")
        except Exception as e:
            builder.writeln(f"[图片加载失败: {str(e)}]")